package cn.always.xiajia.framework.mybatis.core.query;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;

import cn.always.xiajia.framework.common.util.ExtArrayUtils;
import cn.always.xiajia.framework.common.util.ExtCollUtil;
import cn.always.xiajia.framework.common.util.ExtGuavaUtils;
import cn.always.xiajia.framework.common.util.ExtMapUtils;
import cn.always.xiajia.framework.common.util.ExtStringUtil;
import cn.hutool.core.util.ObjectUtil;

public class QueryWrapperX<T> extends QueryWrapper<T> {

	private static final long serialVersionUID = -1522581657938678050L;

	/**
	 * 转换查询器
	 * 
	 * @param param
	 * @return
	 */
	public QueryWrapperX<T> buildByMap(Map<String, Object> param) {
		if (!ExtMapUtils.isEmpty(param)) {
			for (String key : param.keySet()) {
				Object value = param.get(key);
				if (!ExtStringUtil.isBlank(key) && ObjectUtil.isNotEmpty(value)) {
					key = key.replaceAll(" ", "");
					key = key.replace("_OR_", "||").replace("_AND_", "&&");
					if (!isMultiparameter(key)) {
						// 是否多参数
						handleKey(key, this, value);
					} else {
						Object[] values = null;
						if (value != null && value.getClass().isArray()) {
							values = (Object[]) ((Object[]) value);
						} else if (value != null && List.class.isAssignableFrom(value.getClass())) {
							List<Object> list = (List) value;
							values = list.toArray();
						}
						final Object[] valueNews = values;
						key = replaceSymbolInBrackets(key);
						String[] ors = key.split("\\|\\|");
						Map<String, Integer> valueXb = new HashMap<>();
						valueXb.put("xb", 0);
						for (int i = 0; i < ors.length; i++) {
							String or = ors[i];
							String[] ands = or.split("&&");
							this.or();
							for (int j = 0; j < ands.length; j++) {
								String and = ands[j];
								if (and.startsWith("(") && and.endsWith(")")) {
									this.and(a -> {
										String and1 = and.substring(1, and.length() - 1);
										if (and1.contains("&")) {
											String[] subAnds = and1.split("&");
											for (int k = 0; k < subAnds.length; k++) {
												if (and.contains("|")) {
													String[] subOrs = subAnds[k].split("\\|");
													for (int z = 0; z < subOrs.length; z++) {
														handleKey(subOrs[z], a, valueNews[valueXb.get("xb")]);
														valueXb.put("xb", valueXb.get("xb") + 1);
														if (z != subOrs.length - 1) {// 加OR
															a.or();
														}
													}
												} else {
													handleKey(subAnds[k], a, valueNews[valueXb.get("xb")]);
													valueXb.put("xb", valueXb.get("xb") + 1);
												}
											}
										} else if (and1.contains("|")) {
											String[] subOrs = and1.split("\\|");
											for (int k = 0; k < subOrs.length; k++) {
												handleKey(subOrs[k], a, valueNews[valueXb.get("xb")]);
												valueXb.put("xb", valueXb.get("xb") + 1);
												if (k != subOrs.length - 1) {// 加OR
													a.or();
												}
											}
										} else {
											handleKey(and1, a, valueNews[valueXb.get("xb")]);
											valueXb.put("xb", valueXb.get("xb") + 1);
										}
									});
								} else {
									handleKey(and, this, valueNews[valueXb.get("xb")]);
									valueXb.put("xb", valueXb.get("xb") + 1);
								}
							}
						}
					}
				}
			}
		}
		return (QueryWrapperX<T>) this;
	}

	/**
	 * 处理key
	 * 
	 * @param <R>
	 * 
	 * @param key
	 * @return
	 */
	protected void handleKey(String key, QueryWrapper<T> queryWrapper, Object value) {
		if (!ExtStringUtil.isBlank(key)) {
			String[] keys = key.split("_");
			switch (keys[0]) {
			case "EQ":// 等于 =
				queryWrapper.eq(ExtGuavaUtils.toUnderScoreCase(keys[1]), value);
				break;
			case "NE":// 不等于 <> 或者 !=
				queryWrapper.ne(ExtGuavaUtils.toUnderScoreCase(keys[1]), value);
				break;
			case "GT":// 大于 >
				queryWrapper.gt(ExtGuavaUtils.toUnderScoreCase(keys[1]), value);
				break;
			case "GE":// 大于等于 >=
				queryWrapper.ge(ExtGuavaUtils.toUnderScoreCase(keys[1]), value);
				break;
			case "LT":// 小于 <
				queryWrapper.lt(ExtGuavaUtils.toUnderScoreCase(keys[1]), value);
				break;
			case "LE":// 小于等于 <
				queryWrapper.le(ExtGuavaUtils.toUnderScoreCase(keys[1]), value);
				break;
			case "BW":// BETWEEN 值1 AND 值2
				String[] v = value.toString().split(",");
				queryWrapper.between(ExtGuavaUtils.toUnderScoreCase(keys[1]), v[0], v[1]);
				break;
			case "NOTBW":// NOT BETWEEN 值1 AND 值2
				String[] v1 = value.toString().split(",");
				queryWrapper.notBetween(ExtGuavaUtils.toUnderScoreCase(keys[1]), v1[0], v1[1]);
				break;
			case "LIKE":// LIKE ‘%值%’
				queryWrapper.like(ExtGuavaUtils.toUnderScoreCase(keys[1]), value);
				break;
			case "NOTLIKE":// NOT LIKE ‘%值%’
				queryWrapper.notLike(ExtGuavaUtils.toUnderScoreCase(keys[1]), value);
				break;
			case "LIKELEFT":// LIKE ‘%值’
				queryWrapper.likeLeft(ExtGuavaUtils.toUnderScoreCase(keys[1]), value);
				break;
			case "LIKERIGHT":// LIKE ‘值%
				queryWrapper.likeRight(ExtGuavaUtils.toUnderScoreCase(keys[1]), value);
				break;
			case "ISNULL":// 字段 IS NULL
				queryWrapper.isNull(ExtGuavaUtils.toUnderScoreCase(keys[1]));
				break;
			case "ISNOTNULL":// 字段 IS NOT NULL
				queryWrapper.isNotNull(ExtGuavaUtils.toUnderScoreCase(keys[1]));
				break;
			case "IN":// 字段 IN (value1, value2, …)
				if (value != null && value.getClass().isArray()) {
					Object[] values = (Object[]) value;
					List<Object> list = Arrays.stream(values).collect(Collectors.toList());
					queryWrapper.in(ExtGuavaUtils.toUnderScoreCase(keys[1]), list.toString().substring(1, list.toString().length() - 1));
				} else if (value != null && List.class.isAssignableFrom(value.getClass())) {
					List<Object> list = (List) value;
					queryWrapper.in(ExtGuavaUtils.toUnderScoreCase(keys[1]), list.toString().substring(1, list.toString().length() - 1));
				} else {
					queryWrapper.in(ExtGuavaUtils.toUnderScoreCase(keys[1]), value);
				}
				break;
			case "NOTIN":// 字段 NOT IN (value1, value2, …)
				if (value != null && value.getClass().isArray()) {
					Object[] values = (Object[]) value;
					List<Object> list = Arrays.stream(values).collect(Collectors.toList());
					queryWrapper.notIn(ExtGuavaUtils.toUnderScoreCase(keys[1]), list.toString().substring(1, list.toString().length() - 1));
				} else if (value != null && List.class.isAssignableFrom(value.getClass())) {
					List<Object> list = (List) value;
					queryWrapper.notIn(ExtGuavaUtils.toUnderScoreCase(keys[1]), list.toString().substring(1, list.toString().length() - 1));
				} else {
					queryWrapper.notIn(ExtGuavaUtils.toUnderScoreCase(keys[1]), value);
				}
				break;
			case "INSQL":// inSql(“id”, “select id from student where id < 3”)
							// -> id in (select id from student where id < 3)
				queryWrapper.inSql(ExtGuavaUtils.toUnderScoreCase(keys[1]), value.toString());
				break;
			default:
				break;
			}
		}
	}

	/**
	 * 是否多参数
	 * 
	 * @param key
	 * @return
	 */
	protected Boolean isMultiparameter(String key) {
		return key.contains("||") || key.contains("&&") || key.contains("(") || key.contains(")");
	}

	/**
	 * 除了括号
	 * 
	 * @param key
	 * @return
	 */
	protected String replaceSymbolInBrackets(String key) {
		List<Integer[]> braketsList = new ArrayList<>();
		Integer[] brakets = new Integer[2];
		for (int i = 0; i < key.length(); ++i) {
			if (key.charAt(i) == '(') {
				if (brakets[0] != null) {
					throw new IllegalArgumentException("Does not support nesting brackets in search condition key \"" + key + "\".");
				}
				brakets[0] = i;
			} else if (key.charAt(i) == ')') {
				brakets[1] = i;
				if (brakets[0] == null) {
					throw new IllegalArgumentException("Search condition key \"" + key + "\" got the wrong brackets position.");
				}
				braketsList.add(brakets);
				brakets = new Integer[2];
			}
		}
		String temp = "temp_string_for_replace_symbol";
		for (int i = braketsList.size() - 1; i >= 0; --i) {
			Integer[] braketArray = (Integer[]) braketsList.get(i);
			String partKey = key.substring(braketArray[0] + 1, braketArray[1]);
			key = key.replace(partKey, temp);
			partKey = partKey.replaceAll("\\|\\|", "\\|").replaceAll("&&", "&");
			key = key.replace(temp, partKey);
		}
		return key;
	}

	public QueryWrapperX<T> likeIfPresent(String column, String val) {
		if (!ExtStringUtil.isBlank(val)) {
			return (QueryWrapperX<T>) super.like(ExtGuavaUtils.toUnderScoreCase(column), val);
		}
		return this;
	}

	public QueryWrapperX<T> inIfPresent(String column, Collection<?> values) {
		if (ObjectUtil.isAllNotEmpty(values) && !ExtCollUtil.isEmpty(values)) {
			return (QueryWrapperX<T>) super.in(ExtGuavaUtils.toUnderScoreCase(column), values);
		}
		return this;
	}

	public QueryWrapperX<T> inIfPresent(String column, Object... values) {
		if (ObjectUtil.isAllNotEmpty(values) && !ExtArrayUtils.isEmpty(values)) {
			return (QueryWrapperX<T>) super.in(ExtGuavaUtils.toUnderScoreCase(column), values);
		}
		return this;
	}

	public QueryWrapperX<T> eqIfPresent(String column, Object val) {
		if (ObjectUtil.isNotEmpty(val)) {
			return (QueryWrapperX<T>) super.eq(ExtGuavaUtils.toUnderScoreCase(column), val);
		}
		return this;
	}

	public QueryWrapperX<T> neIfPresent(String column, Object val) {
		if (ObjectUtil.isNotEmpty(val)) {
			return (QueryWrapperX<T>) super.ne(ExtGuavaUtils.toUnderScoreCase(column), val);
		}
		return this;
	}

	public QueryWrapperX<T> gtIfPresent(String column, Object val) {
		if (val != null) {
			return (QueryWrapperX<T>) super.gt(ExtGuavaUtils.toUnderScoreCase(column), val);
		}
		return this;
	}

	public QueryWrapperX<T> geIfPresent(String column, Object val) {
		if (val != null) {
			return (QueryWrapperX<T>) super.ge(ExtGuavaUtils.toUnderScoreCase(column), val);
		}
		return this;
	}

	public QueryWrapperX<T> ltIfPresent(String column, Object val) {
		if (val != null) {
			return (QueryWrapperX<T>) super.lt(ExtGuavaUtils.toUnderScoreCase(column), val);
		}
		return this;
	}

	public QueryWrapperX<T> leIfPresent(String column, Object val) {
		if (val != null) {
			return (QueryWrapperX<T>) super.le(ExtGuavaUtils.toUnderScoreCase(column), val);
		}
		return this;
	}

	public QueryWrapperX<T> betweenIfPresent(String column, Object val1, Object val2) {
		if (val1 != null && val2 != null) {
			return (QueryWrapperX<T>) super.between(ExtGuavaUtils.toUnderScoreCase(column), val1, val2);
		}
		if (val1 != null) {
			return (QueryWrapperX<T>) ge(ExtGuavaUtils.toUnderScoreCase(column), val1);
		}
		if (val2 != null) {
			return (QueryWrapperX<T>) le(ExtGuavaUtils.toUnderScoreCase(column), val2);
		}
		return this;
	}

	public QueryWrapperX<T> betweenIfPresent(String column, Object[] values) {
		Object val1 = ExtArrayUtils.get(values, 0);
		Object val2 = ExtArrayUtils.get(values, 1);
		return betweenIfPresent(ExtGuavaUtils.toUnderScoreCase(column), val1, val2);
	}

	// ========== 重写父类方法，方便链式调用 ==========

	@Override
	public QueryWrapperX<T> eq(boolean condition, String column, Object val) {
		super.eq(condition, ExtGuavaUtils.toUnderScoreCase(column), val);
		return this;
	}

	@Override
	public QueryWrapperX<T> eq(String column, Object val) {
		super.eq(ExtGuavaUtils.toUnderScoreCase(column), val);
		return this;
	}

	@Override
	public QueryWrapperX<T> orderByDesc(String column) {
		super.orderByDesc(true, ExtGuavaUtils.toUnderScoreCase(column));
		return this;
	}

	@Override
	public QueryWrapperX<T> last(String lastSql) {
		super.last(lastSql);
		return this;
	}

	@Override
	public QueryWrapperX<T> in(String column, Collection<?> coll) {
		super.in(ExtGuavaUtils.toUnderScoreCase(column), coll);
		return this;
	}
}
